/* @flow */

import config from "../config";
import { initProxy } from "./proxy";
import { initState } from "./state";
import { initRender } from "./render";
import { initEvents } from "./events";
import { mark, measure } from "../util/perf";
import { initLifecycle, callHook } from "./lifecycle";
import { initProvide, initInjections } from "./inject";
import { extend, mergeOptions, formatComponentName } from "../util/index";

let uid = 0;

export function initMixin(Vue: Class<Component>) {
  // init初始化函数
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this;
    // 参数初始化 begin
    vm._uid = uid++;

    let startTag, endTag;
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== "production" && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`;
      endTag = `vue-perf-end:${vm._uid}`;
      mark(startTag);
    }
    // 参数初始化 end
    // a flag to avoid this being observed 一个避免被观察到的标志
    vm._isVue = true;
    // merge options 合并options参数
    // 当执行new vue 时候, option._iscomponent为false，走到else逻辑分支
    if (options && options._isComponent) { //判断是否是组件
    // 执行过 src/core/vdom/create-component.js 文件中 createComponentInstanceForVnode 函数后， _isComponent为true
      // 优化内部组件实例化
      // 由于动态选项合并非常慢，内部组件options都不进行特殊处理
      // 初始化内部组件
      initInternalComponent(vm, options);
    } else {
      // 非组件时，需要合并options
      // 合并参数，将两个对象合并成一个对象，将父子对象合并一起，并优先获取子对象，如果没有则获取子对象
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }
    /* 初始化函数 */
    if (process.env.NODE_ENV !== "production") {
      initProxy(vm);
    } else {
      vm._renderProxy = vm;
    }
    // expose real self 暴露真实的self对象
    vm._self = vm;
    initLifecycle(vm); // 初始化生命周期
    initEvents(vm); // 初始化事件
    initRender(vm); // 初始化渲染函数render
    callHook(vm, "beforeCreate"); //触发beforeCreate、钩子函数
    initInjections(vm); // 在data/options之前初始化 注入injection进来的数据
    initState(vm); // src/core/instance/state.js， 该方法主要是转换data中的数据到template中
    initProvide(vm); // 在data/options之后 处理provide提供数据。provide是一个对象或返回对象的函数，该对象包含可注入其子孙的属性，多用于组件间通信
    callHook(vm, "created"); // 触发created钩子函数

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== "production" && config.performance && mark) {
      vm._name = formatComponentName(vm, false);
      mark(endTag);
      measure(`vue ${vm._name} init`, startTag, endTag);
    }
    // 判断vm实例是否存在vm.$options.el，存在就将vm挂载到dom节点
    if (vm.$options.el) {
      // vue的mount()为手动挂载
      // 执行vm的$mount构造之后，dom才完成渲染。在此阶段之前获取不到dom对象
      // new Vue时，el和$mount没有区别
      // $mount挂载的目标就是把模板渲染成最终的DOM结构
      vm.$mount(vm.$options.el);
      // 重点分析src/platforms/web/entry-runtime-with-compiler.js 的19行$mount
    }
  };
}
/**
 * @description: 初始化内部组件
 * @param {*} vm：vue实例对象；option：对象的opitons属性
 * @return {*}
 */
export function initInternalComponent(
  vm: Component,
  options: InternalComponentOptions
) {
  const opts = (vm.$options = Object.create(vm.constructor.options)); //vm参数
  // doing this because it's faster than dynamic enumeration.
  // 使用下面这种赋值方式比动态枚举更快
  const parentVnode = options._parentVnode;
  opts.parent = options.parent; //组件的父节点
  opts._parentVnode = parentVnode; //组件的虚拟vnode，父节点

  const vnodeComponentOptions = parentVnode.componentOptions; //组件参数options
  opts.propsData = vnodeComponentOptions.propsData; //组件props
  opts._parentListeners = vnodeComponentOptions.listeners; //组件事件
  opts._renderChildren = vnodeComponentOptions.children; //组件子节点
  opts._componentTag = vnodeComponentOptions.tag; //组件标签tag

  if (options.render) { //渲染函数
    opts.render = options.render;
    opts.staticRenderFns = options.staticRenderFns; //静态渲染函数
  }
}
// 解析new Vue constructor上的options拓展参数属性的 合并 过滤去重数据
export function resolveConstructorOptions(Ctor: Class<Component>) {
  let options = Ctor.options;
  // 有super属性，说明Ctor是Vue.extend构建的子类 
  if (Ctor.super) { // 父类
    const superOptions = resolveConstructorOptions(Ctor.super); // 继续回调父类，表示继承父类
    const cachedSuperOptions = Ctor.superOptions;// Vue构造函数上的options,如directives,filters,....
    if (superOptions !== cachedSuperOptions) { //判断父类的options不等于子类的options的时候
      // super option changed, 父类option改变
      // need to resolve new options. 需要处理新的options
      Ctor.superOptions = superOptions; //给父类选项赋值Ctor.superOptions
      // check if there are any late-modified/attached options (#4976)
      // 解决修改选项 转义数据 合并属性
      const modifiedOptions = resolveModifiedOptions(Ctor);
      // update base extend options 更新基础的扩展option
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions);
      }
      // 优先取Ctor.extendOptions，将两个对象合并成一个对象，将父子对象合并，并优先获取子对象
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);
      if (options.name) { //如果组件有name属性，表示该组件的名称
        options.components[options.name] = Ctor;
      }
    }
  }
  return options;
}
// 解决修改options转义数据 合并数据
function resolveModifiedOptions(Ctor: Class<Component>): ?Object {
  let modified;
  const latest = Ctor.options; //获取选项
  const sealed = Ctor.sealedOptions; //获取子类选项
  for (const key in latest) { //遍历最新选项
    if (latest[key] !== sealed[key]) { //如果新选项不等于子选项
      if (!modified) modified = {};
      modified[key] = latest[key];
    }
  }
  return modified;
}
